﻿var options, storage, ctx;

if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = (function () {
        'use strict';

        return window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) {
                    window.setTimeout(callback, 16.7);
                };
    }());
}

function BabySmash() {
    'use strict';

    var shapeCanvas, figureAssets, handledKey,
        canvasHistory, mouseCircles, mouseCursor, soundExtension,
        isDrawing = false,
        renderHandle = null,
        mouseX = -200,
        mouseY = -200,
        scaleFactor = 1.0,
        lastEvent = { x: -200, y: -200 },
        sounds = [
            'sounds/giggle',
            'sounds/babylaugh',
            'sounds/babygigl2',
            'sounds/ccgiggle',
            'sounds/laughingmice',
            'sounds/scooby2'
        ],
        someColors = [
            { soundFile: 'sounds/red', hex: '#FF0000', darkHex: '#980000' },
            { soundFile: 'sounds/blue', hex: '#00ffff', darkHex: '#0000ff' },
            { soundFile: 'sounds/yellow', hex: '#ffff00', darkHex: '#98ff00' },
            { soundFile: 'sounds/green', hex: '#00ff00', darkHex: '#006736' },
            { soundFile: 'sounds/purple', hex: '#ff00ff', darkHex: '#9d005c' },
            { soundFile: 'sounds/pink', hex: '#F0ECEA', darkHex: '#FF00F9' },
            { soundFile: 'sounds/orange', hex: '#ffff00', darkHex: '#f05923' },
            { soundFile: 'sounds/tan', hex: '#F7DFBA', darkHex: '#A68B66' },
            { soundFile: 'sounds/gray', hex: '#8E8E8E', darkHex: '#5B5B5B' }
        ];

    var noOp = function (e) {
        e.preventDefault();
    };

    var isLetterOrDigit = function (value) {
        var types = ['Lu', 'Ll', 'Lt', 'Lm', 'Nd'],
            i;

        for (i = 0; i < types.length; i += 1) {
            if (XRegExp('\\p{' + types[i] + '}').test(value)) {
                return true;
            }
        }

        return false;
    };

    var randomFromTo = function (from, to) {
        return Math.floor(Math.random() * (to - from + 1) + from);
    };

    var keyPress = function (e) {
        var ch;

        ch = String.fromCharCode(e.which);

        if (e.ctrlKey && e.shiftKey && (e.which === 79 || e.which === 15)) {
            e.preventDefault();
            handledKey = true;
            showOptions();
            return;
        }

        if (isLetterOrDigit(ch)) {
            if (options.forceUppercase()) {
                ch = ch.toUpperCase();
            }
            var letter = new letterTemplate(getRandomSoundFile(), ch);
            var clr = getRandomColor();
            letter.color = clr.hex;
            letter.darkColor = clr.darkHex;
            letter.pointX = randomFromTo(0, window.innerWidth) / scaleFactor;
            letter.pointY = randomFromTo(0, window.innerHeight) / scaleFactor;
            canvasHistory.push(letter);
            e.preventDefault();
            handledKey = true;

            if (canvasHistory.length > options.maximumShapes()) {
                canvasHistory.shift();
            }

            if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
                playSound('sounds/' + ch);
            } else {
                playSound();
            }
        }
    };

    var keyUp = function (e) {
        if (!handledKey) {
            e.preventDefault();
            addRandomShape(randomFromTo(0, window.innerWidth) / scaleFactor, randomFromTo(0, window.innerHeight) / scaleFactor);
        }
        handledKey = false;
    };

    var mouseDown = function (e) {
        e.preventDefault();
        lastEvent = { x: e.clientX, y: e.clientY };
        if (isDrawing || options.clicklessDraw()) {
            return;
        }

        mouseDraw(e.clientX / scaleFactor, e.clientY / scaleFactor);
        isDrawing = true;
        playSoundResource('sounds/smallbumblebee');
    };

    var mouseMove = function (e) {
        e.preventDefault();
        mouseX = e.clientX / scaleFactor;
        mouseY = e.clientY / scaleFactor;
        if (isDrawing || options.clicklessDraw()) {
            if (Math.abs(lastEvent.x - e.clientX) > 5 || Math.abs(lastEvent.y - e.clientY) > 5) {
                mouseDraw(mouseX, mouseY);
            }
        }
    };

    var mouseUp = function (e) {
        isDrawing = false;
    };

    var tapped = function (e) {
        if (Math.abs(lastEvent.x - e.clientX) <= 5 && Math.abs(lastEvent.y - e.clientY) <= 5) {
            addRandomShape(e.clientX / scaleFactor, e.clientY / scaleFactor);
        }
    };

    var addRandomShape = function (x, y) {
        var asset = figureAssets[randomFromTo(0, figureAssets.length - 1)];
        var shapeClone = new figureTemplate(asset.renderFunction, asset.soundFile);
        var clr = getRandomColor();
        shapeClone.color = clr.hex;
        shapeClone.darkColor = clr.darkHex;
        shapeClone.pointX = x;
        shapeClone.pointY = y;
        canvasHistory.push(shapeClone);

        if (canvasHistory.length > options.maximumShapes()) {
            canvasHistory.shift();
        }

        playSound([clr.soundFile, shapeClone.soundFile]);
    };

    var mouseDraw = function (x, y) {
        var circle = {
            centerX: x,
            centerY: y,
            color: getRandomColor(),
            fill: ctx.createRadialGradient(25, 25, 0, 25, 25, 25)
        };
        circle.fill.addColorStop(0, circle.color.hex);
        circle.fill.addColorStop(1, circle.color.darkHex);

        mouseCircles.push(circle);

        if (mouseCircles.length > 30) {
            mouseCircles.shift();
        }
    };

    var resize = function () {
        document.getElementById('main').width = window.innerWidth;
        document.getElementById('main').height = window.innerHeight;

        scaleFactor = Math.min(window.innerWidth / 1024, window.innerHeight / 1280);
        ctx.scale(scaleFactor, scaleFactor);

        loadFont();
    };

    var getRandomColor = function () {
        return someColors[randomFromTo(0, someColors.length - 1)];
    };

    var playSound = function (s) {
        if (options.soundStyle() === 'Laughter' || s === null) {
            playLaughter();
        } else if (options.soundStyle() === 'Speech') {
            speakString(s);
        }
    };

    var playLaughter = function () {
        var file = getRandomSoundFile();
        playSoundResource(file);
    };

    var speakString = function (s) {
        playSoundResource(s);
    };

    var playSoundResource = function (file) {
        var snd;

        if (file instanceof Array) {
            snd = new Audio(file[0] + soundExtension);
            snd.onended = function () {
                this.onended = function () { };
                this.src = file[1] + soundExtension;
                this.play();
            };
            snd.play();
        } else {
            snd = new Audio(file + soundExtension);
            snd.play();
        }
    };

    var getRandomSoundFile = function () {
        return sounds[randomFromTo(0, 5)];
    };

    var showOptions = function () {
        if (document.getElementById('options')) {
            detachEvents()
            document.getElementById('maximumShapes').value = options.maximumShapes();
            document.getElementById('sounds').value = options.soundStyle();
            document.getElementById('cursor').value = options.cursor();
            document.getElementById('characterFont').value = options.characterFont();
            document.getElementById('forceUppercase').checked = options.forceUppercase();
            document.getElementById('facesOnShapes').checked = options.facesOnShapes();
            document.getElementById('clicklessDraw').checked = options.clicklessDraw();
            document.getElementById('fadeShapes').checked = options.fadeShapes();
            document.getElementById('fadeShapesAfter').value = options.fadeShapesAfter();
            document.getElementById('options').style.display = 'inherit';
        }
    };

    this.saveOptions = function () {
        options.save();
        document.getElementById('options').style.display = 'none';
        attachEvents();
        loadCursor();
        loadFont();
    };

    var figureTemplate = function (drawFunction, soundFile) {
        this.soundFile = soundFile;
        this.draw = drawFunction;
        this.color = null;
        this.darkColor = null;
        this.pointX = null;
        this.pointY = null;
        this.alpha = 1.0;
        this.eyesAlpha = 1;
        this.keyFrame = 0;

        this.setAlpha = function (canvasCtx) {
            canvasCtx.globalAlpha = this.alpha;
        };

        this.setAnimationAlpha = function (canvasCtx) {
            var alpha = this.eyesAlpha;
            canvasCtx.globalAlpha = Math.min(canvasCtx.globalAlpha, alpha);
        };
    };

    var letterTemplate = function (soundFile, character) {
        this.soundFile = soundFile;
        this.draw = renderLetter;
        this.color = null;
        this.darkColor = null;
        this.pointX = null;
        this.pointY = null;
        this.alpha = 1.0;
        this.character = character;
    };

    var loadCursor = function () {
        if (options.cursor() === 'Hand') {
            mouseCursor.src = 'images/FunCursor2.svg';
        } else {
            mouseCursor.src = 'images/FunCursor1.svg';
        }
    };

    var loadFont = function () {
        ctx.font = 'bold 300px ' + options.characterFont();
    };

    var drawFrame = function () {
        var i;

        ctx.clearRect(0, 0, shapeCanvas.width / scaleFactor, shapeCanvas.height / scaleFactor);

        for (i = 0; i < canvasHistory.length; i += 1) {
            canvasHistory[i].draw();
        }

        if (options.fadeShapes()) {
            ageHistory();
        }
    };

    var ageHistory = function () {
        var i;

        for (i = canvasHistory.length - 1; i >= 0; i -= 1) {
            canvasHistory[i].alpha -= 0.003;

            if (canvasHistory[i].alpha <= 0) {
                canvasHistory.remove(i);
            }
        }
    };

    var keyFrameAnimation = function () {
        var i;

        for (i = 0; i < canvasHistory.length; i += 1) {
            var frame = canvasHistory[i].keyFrame;

            if (frame === 146 || frame === 147) {
                canvasHistory[i].eyesAlpha = 0;
            } else {
                canvasHistory[i].eyesAlpha = 1;
            }

            frame++;
            if (frame > 480) {
                frame = 0;
            }
            canvasHistory[i].keyFrame = frame;
        }
    };

    // Array Remove - By John Resig (MIT Licensed)
    Array.prototype.remove = function (from, to) {
        var rest = this.slice((to || from) + 1 || this.length);
        this.length = from < 0 ? this.length + from : from;
        return this.push.apply(this, rest);
    };

    var renderLetter = function () {
        var left = this.pointX - 103, top = this.pointY - 103;
        ctx.translate(left, top);

        var radialgradient = ctx.createRadialGradient(120, 60, 0, 120, 60, 129);
        radialgradient.addColorStop(0, this.color);
        radialgradient.addColorStop(1, this.darkColor);

        ctx.globalAlpha = this.alpha;
        ctx.strokeStyle = '#ff000000';
        ctx.lineWidth = 10;
        ctx.fillStyle = radialgradient;
        ctx.fillText(this.character, 0, 0);
        ctx.strokeText(this.character, 0, 0);

        ctx.translate(-left, -top);
    };

    var drawCircles = function (x, y) {
        var i;

        ctx.globalAlpha = 1;

        for (i = 0; i < mouseCircles.length; i += 1) {
            var currentCircle = mouseCircles[i];

            ctx.translate(currentCircle.centerX - 25, currentCircle.centerY - 25);

            ctx.fillStyle = currentCircle.fill;
            ctx.beginPath();
            ctx.arc(25, 25, 25, 0, Math.PI * 2, false);
            ctx.closePath();
            ctx.fill();

            ctx.translate(-(currentCircle.centerX - 25), -(currentCircle.centerY - 25));
        }

        ctx.drawImage(mouseCursor, x, y);
    };

    var renderLoop = function (timestamp) {
        drawFrame();
        keyFrameAnimation();
        drawCircles(mouseX, mouseY);

        renderHandle = window.requestAnimationFrame(renderLoop);
    };

    var attachEvents = function () {
        if (navigator.msPointerEnabled) {
            document.getElementById('main').addEventListener("MSPointerDown", mouseDown, false);
            document.addEventListener("MSPointerMove", mouseMove, false);
            document.addEventListener("MSPointerUp", mouseUp, false);
            document.addEventListener("keypress", keyPress, false);
            document.addEventListener("keyup", keyUp, false);
            document.getElementById('main').addEventListener("click", tapped, false);
            document.addEventListener("MSGestureHold", noOp, false);
        } else {
            $(document).on("vmousedown", mouseDown);
            $(document).on("vmousemove", mouseMove);
            $(document).on("vmouseup", mouseUp);
            $(document).on("keypress", keyPress);
            $(document).on("keyup", keyUp);
            $('#main').on("tap", tapped);
        }
        window.addEventListener("resize", resize, true);
        window.addEventListener('contextmenu', noOp, false);
        window.addEventListener('dragstart', noOp, false);
        window.addEventListener('selectstart', noOp, false);
    };

    var detachEvents = function () {
        if (navigator.msPointerEnabled) {
            document.getElementById('main').removeEventListener("MSPointerDown", mouseDown, false);
            document.removeEventListener("MSPointerMove", mouseMove, false);
            document.removeEventListener("MSPointerUp", mouseUp, false);
            document.removeEventListener("keypress", keyPress, false);
            document.removeEventListener("keyup", keyUp, false);
            document.removeEventListener("MSGestureHold", noOp, false);
        } else {
            $(document).off('vmousedown', mouseDown);
            $(document).off('vmousemove', mouseMove);
            $(document).off('vmouseup', mouseUp);
            $(document).off('keypress', keyPress);
            $(document).off('keyup', keyUp);
            $('#main').off("tap", tapped);
        }
        window.removeEventListener('resize', resize, true);
        window.removeEventListener('contextmenu', noOp, false);
        window.removeEventListener('dragstart', noOp, false);
        window.removeEventListener('selectstart', noOp, false);
    };

    this.initialize = function () {
        options = new BabySmashOptions();
        storage = window.localStorage;
        shapeCanvas = document.getElementById('main');
        ctx = shapeCanvas.getContext('2d');
        figureAssets = [];
        figureAssets[0] = { renderFunction: renderStar, soundFile: "shapes/sounds/star" };
        figureAssets[1] = { renderFunction: renderCircle, soundFile: "shapes/sounds/circle" };
        figureAssets[2] = { renderFunction: renderHeart, soundFile: "shapes/sounds/heart" };
        figureAssets[3] = { renderFunction: renderHexagon, soundFile: "shapes/sounds/hexagon" };
        figureAssets[4] = { renderFunction: renderRectangle, soundFile: "shapes/sounds/rectangle" };
        figureAssets[5] = { renderFunction: renderSquare, soundFile: "shapes/sounds/square" };
        figureAssets[6] = { renderFunction: renderTrapezoid, soundFile: "shapes/sounds/trapezoid" };
        figureAssets[7] = { renderFunction: renderTriangle, soundFile: "shapes/sounds/triangle" };
        canvasHistory = [];
        mouseCircles = [];
        mouseCursor = new Image();
        ctx.textBaseline = 'top';
        loadCursor();
        loadFont();

        if (document.createElement('audio').canPlayType('audio/mpeg')) {
            soundExtension = ".mp3";
        } else {
            soundExtension = ".ogg";
        }

        attachEvents();
        resize();

        playSoundResource('sounds/EditedJackPlaysBabySmash');

        renderHandle = window.requestAnimationFrame(renderLoop);
    };

    this.pause = function () {
        detachEvents();
    };

    this.resume = function () {
        attachEvents();
        loadCursor();
        loadFont();
    };
}

function BabySmashOptions() {
}

BabySmashOptions.prototype.maximumShapes = function () {
    if (!storage.maximumShapes) {
        return 100;
    } else {
        return storage.maximumShapes;
    }
};

BabySmashOptions.prototype.soundStyle = function () {
    if (!storage.soundStyle) {
        return 'Speech';
    } else {
        return storage.soundStyle;
    }
};

BabySmashOptions.prototype.cursor = function () {
    if (!storage.cursor) {
        return 'Hand';
    } else {
        return storage.cursor;
    }
};

BabySmashOptions.prototype.characterFont = function () {
    if (!storage.characterFont) {
        return 'Arial';
    } else {
        return storage.characterFont;
    }
};

BabySmashOptions.prototype.forceUppercase = function () {
    if (storage.forceUppercase === null) {
        return true;
    } else {
        return storage.forceUppercase === 'true' ? true : false;
    }
};

BabySmashOptions.prototype.facesOnShapes = function () {
    if (storage.facesOnShapes === null) {
        return true;
    } else {
        return storage.facesOnShapes === 'true' ? true : false;
    }
};

BabySmashOptions.prototype.clicklessDraw = function () {
    if (!storage.clicklessDraw) {
        return true;
    } else {
        return storage.clicklessDraw === 'true' ? true : false;
    }
};

BabySmashOptions.prototype.fadeShapes = function () {
    if (!storage.fadeShapes) {
        return true;
    } else {
        return storage.fadeShapes === 'true' ? true : false;
    }
};

BabySmashOptions.prototype.fadeShapesAfter = function () {
    if (!storage.fadeShapesAfter) {
        return 16;
    } else {
        return storage.fadeShapesAfter;
    }
};

BabySmashOptions.prototype.save = function () {
    storage.maximumShapes = document.getElementById('maximumShapes').value;
    storage.soundStyle = document.getElementById('sounds').value;
    storage.cursor = document.getElementById('cursor').value;
    storage.characterFont = document.getElementById('characterFont').value;
    storage.forceUppercase = document.getElementById('forceUppercase').checked;
    storage.facesOnShapes = document.getElementById('facesOnShapes').checked;
    storage.clicklessDraw = document.getElementById('clicklessDraw').checked;
    storage.fadeShapes = document.getElementById('fadeShapes').checked;
    storage.fadeShapesAfter = document.getElementById('fadeShapesAfter').value;
};

var babySmash = new BabySmash();

window.addEventListener("DOMContentLoaded", babySmash.initialize, false);